package com.mdp.oauth2.server.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.mdp.oauth2.server.cache.ClientCache;
import com.mdp.oauth2.server.entity.OauthRegisteredClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.Map;

@Service
public class JdbcRegisteredClientRepository implements RegisteredClientRepository {

    @Autowired
    OauthRegisteredClientService clientService;

    @Autowired
    public ClientCache clientCache;

    @Override
    public RegisteredClient findById(String id) {
        RegisteredClient registeredClientCache=clientCache.getById(id);
        if(registeredClientCache!=null){
            return registeredClientCache;
        }
        QueryWrapper<OauthRegisteredClient> qw=new QueryWrapper<>();
        qw.eq("id",id);
        Map<String,Object> client= clientService.getMap(qw);
        if(client==null || client.isEmpty()){
            return null;
        }
        registeredClientCache = this.buildRegisteredClientFromMap(client);
        clientCache.put(registeredClientCache);
        return registeredClientCache;
    }

    @Override
    public RegisteredClient findByClientId(String clientId) {
        RegisteredClient registeredClientCache=clientCache.getByClientId(clientId);
        if(registeredClientCache!=null){
            return registeredClientCache;
        }
        QueryWrapper<OauthRegisteredClient> qw=new QueryWrapper<>();
        qw.eq("client_id",clientId);
        Map<String,Object> client= clientService.getMap(qw);
        if(client==null || client.isEmpty()){
            return null;
        }
        registeredClientCache = this.buildRegisteredClientFromMap(client);
        clientCache.put(registeredClientCache);
        return registeredClientCache;
    }

    String notnullField(String fieldValue,String defaultValue){
        if(fieldValue==null){
            return defaultValue;
        }else {
            return fieldValue;
        }
    }
    boolean booleanField(String fieldValue,boolean defaultValue){
        if(fieldValue==null){
            return defaultValue;
        }else {
            return "1".equals(fieldValue);
        }
    }
    Integer integerField(String fieldValue,int defaultValue){
        if(fieldValue==null){
            return defaultValue;
        }else {
            return Integer.parseInt(fieldValue);
        }
    }
    RegisteredClient buildRegisteredClientFromMap(Map<String,Object> client){
        String authorizationGrantTypes= notnullField( (String) client.get("authorizationGrantTypes"),"");
        String redirectUris= notnullField( (String) client.get("redirectUris") ,"");
        String scopes= notnullField(  (String) client.get("scopes") ,"all");
        boolean requireProofKey= booleanField(  (String) client.get("requireProofKey"),false );
        boolean requireUserConsent= booleanField(  (String) client.get("requireUserConsent"),false  );
        Integer accessTokenTimeToLive= integerField(  (String) client.get("accessTokenTimeToLive") ,24 );
        boolean enableRefreshTokens= booleanField(  (String) client.get("enableRefreshTokens"),true  );
        boolean reuseRefreshTokens= booleanField(  (String) client.get("reuseRefreshTokens") ,true );
        Integer refreshTokenTimeToLive= integerField(  (String) client.get("refreshTokenTimeToLive") ,24 );
        String authorizationMethod=notnullField(  (String) client.get("authorizationMethod"),"basic" );
        RegisteredClient registeredClient=RegisteredClient
                .withId((String) client.get("id"))
                .clientId((String) client.get("clientId"))
                .clientSecret((String) client.get("clientSecret"))
                .authorizationGrantTypes(set-> {
                    for (String s : authorizationGrantTypes.split(",")) {
                        set.add(new AuthorizationGrantType(s));
                    }
                })
                .redirectUris(set-> {
                    for (String s : redirectUris.split(",")) {
                        set.add(s);
                    }
                })
                .scopes(set->{
                    for (String s : scopes.split(",")) {
                        set.add(s);
                    }
                })
                .tokenSettings(x->{
                    x.reuseRefreshTokens(reuseRefreshTokens);
                    x.enableRefreshTokens(enableRefreshTokens);
                    x.accessTokenTimeToLive(Duration.ofHours(accessTokenTimeToLive));
                    x.refreshTokenTimeToLive(Duration.ofHours(refreshTokenTimeToLive));
                })
                .clientSettings(x->{
                    x.requireProofKey(requireProofKey);
                    x.requireUserConsent(requireUserConsent);
                })
                .clientAuthenticationMethod(new ClientAuthenticationMethod(authorizationMethod))
                .build();
        return registeredClient;
    }

}
